#include "graphdl.h"

const uint32 kGraphDLSize = 10000000;

GraphDL::GraphDL(double theta, double alpha) {
  dl_ = 0;
  total_count_ = 0;
  theta_ = theta;
  alpha_ = alpha;
}

void GraphDL::AddObject(Object * o) {
  // cout << "::AddObject " << ToString(o) << endl;
  if (!IsReusable(o)) {
    AddDefinition(o);
    return;
  }
  AddReusableObject(o);
}

void GraphDL::RemoveObject(Object * o) {
  if (!IsReusable(o)) {
    RemoveDefinition(o);
    return;
  }
  RemoveReusableObject(o);
}

// Reuse non-arrays or Lambda Definitions
bool GraphDL::IsReusable(Object * o) {
  return false;
  return true;
  if (GetType(o) != ARRAY_TYPE) return true;
  if (o->ArraySize() == 3 && o->Ref(0) == LAMBDA) return true;
  return false;
}

// Add a reusable object
// Figure out if you need to add it's definition
// Add a reference
// In our current method, chances of making a new table = 1 / total_count + 1


void GraphDL::AddReusableObject(Object * o) {
  uint64 h = Hash (o);
  uint32 &count = counts_[h]; // % kGraphDLSize];
  dl_ += DMLn(total_count_ + theta_);
  total_count_++;
  // dl_ += MLn(total_count_);
  
  // You are starting a new table!
  if (count == 0) {
    dl_ -= DMLn(theta_ + count * alpha_);
    AddDefinition(o);
  } else {
    // dl_ -= MLn(count - 1);
    dl_ -= DMLn(count - alpha_);
  }
  count++;
}

void GraphDL::RemoveReusableObject(Object * o) {
  // cout << "::RemoveObject " << ToString(o) << endl;
  uint64 h = Hash (o);
  uint32 &count = counts_[h]; // % kGraphDLSize];
  if (!(count > 0)) {
    cout << "count:" << count << endl;
    cout << ToString(o) << endl;
    CHECK(false);
  }

  //  dl_ -= MLn(total_count_);
  count--;
  total_count_--;
  dl_ -= DMLn(total_count_ + theta_);
 
  // You were the only one at your table!
  if (count == 0) {
    dl_ += DMLn(theta_ + count * alpha_);
    RemoveDefinition(o);
  } else {
    dl_ += DMLn(count - alpha_);
    // dl_ += MLn(count);
  }
}


void GraphDL::AddDefinition(Object *o) {
  switch (GetType(o)) {
  case ARRAY_TYPE:
    dl_ += IntegerDL(o->ArraySize());
    for (uint64 c=0; c<o->ArraySize(); c++) {
      AddObject(o->Ref(c));
    }  
    break;
  case INTEGER:
    dl_ += IntegerDL(GetInteger(o));
    break;
  case SYMBOL:
    dl_ += 1000000;
  default:
    break;
  }
}   

void GraphDL::RemoveDefinition(Object *o) {
  switch (GetType(o)) {
  case ARRAY_TYPE:
    dl_ -= IntegerDL(o->ArraySize());
    for (uint64 c=0; c<o->ArraySize(); c++) {
      RemoveObject(o->Ref(c));
    }  
    break;
  case INTEGER:
    dl_ -= IntegerDL(GetInteger(o));
    break;
  case SYMBOL:
    dl_ -= 1000000;
  default:
    break;
  }
}   


